#include <event2/event.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <iostream>
#include <string.h>
#include <string>
#include <zlib.h>
#ifndef _WIN32
#include <signal.h>
#endif
using namespace std;

class Status{
public:
        bool start = false;
        FILE *fp = NULL;
        z_stream *p = NULL;
        int recvNum = 0;
        int writeNum = 0;

        ~Status(){
                if(fp){
                        fclose(fp);
                        fp = NULL;
                }

                if(p){
                        inflateEnd(p);
                        p = NULL;
                }
                //delete p;
        }
};

bufferevent_filter_result  filter_in(evbuffer *src, evbuffer *dst,  ev_ssize_t dst_limit, 
                bufferevent_flush_mode mode, void *ctx){
        Status *status = (Status *)ctx;

        //1 接受客户端发送文件名
        if(!status->start){
                char data[1024] = {0};
                int len = evbuffer_remove(src, data, sizeof(data)-1);
                cout<<"server recv "<<data<<endl;
                evbuffer_add(dst, data, len);
                return BEV_OK;
        }
        
        //解压
        evbuffer_iovec v_in[1];
        //读取数据 不清理缓冲
        int n = evbuffer_peek(src, -1, NULL, v_in, 1);
        if(n<=0){
                //没有数据 不会进入读取回调
                return BEV_NEED_MORE;
        }

        //zlib 输入数据大小
        status->p->avail_in  = v_in[0].iov_len;
        //输入空间地址
        status->p->next_in = (Byte *)v_in[0].iov_base;

        //申请输出空间大小
        evbuffer_iovec v_out[1];
        evbuffer_reserve_space(dst, 4096, v_out, 1);

        //zlib 输出空间大小
        status->p->avail_out = v_out[0].iov_len;
        //zlib 输出空间地址
        status->p->next_out = (Byte *)v_out[0].iov_base;

        //解压
        int ret = inflate(status->p, Z_SYNC_FLUSH);
        if(ret != Z_OK){
                cerr<<" deflate failed! "<<endl;
                return BEV_ERROR;
        } 

        //解压用了多少数据 从src evbuffer中移除
        //status->p->avail_in 未处理的数据大小
        int nread = v_in[0].iov_len - status->p->avail_in;
        //解压后数据大小 传入 dst evbuffer
        //status->p->avail_out  剩余空间大小
        int nwrite = v_out[0].iov_len - status->p->avail_out;

         //移除src evbuffer中数据
        evbuffer_drain(src, nread);

        //传入 dst evbuffer
        v_out[0].iov_len = nwrite;
        evbuffer_commit_space(dst, v_out, 1);
        cout<<"server nread = "<<nread<<" nwrite = "<<nwrite<<endl;
        status->recvNum += nread;
        status->writeNum += nwrite;

        return BEV_OK;
}

void read_cb(bufferevent *bev, void *ctx){
        Status *status = (Status *)ctx;
        if(!status->start){
                //001接收文件名
                char data[1024] = { 0 };
                bufferevent_read(bev, data, sizeof(data) - 1);
                //status->filename = data;
                string out  = "out/";
                out += data;
                status->fp = fopen(out.c_str(), "wb");
                if(!status->fp){
                        cout<<"server open "<<out<< "failed "<< endl;
                        return ;
                }
                //002 回复OK
                bufferevent_write(bev,"OK", 2 );
                status->start = true;
                return;
        }

        do{        
                //写入文件
                char data[1024] = { 0 };
                int len = bufferevent_read(bev, data, sizeof(data));
                if(len >= 0){
                        fwrite(data, 1, len, status->fp);
                        fflush(status->fp);
                }
        } while (evbuffer_get_length(bufferevent_get_input(bev)) > 0);
        
}

void event_cb(bufferevent *bev, short what, void *ctx){
        cout << "server event_cb "<<what<<endl;
        if(what & BEV_EVENT_EOF){ //客户端断开
                cout<<  "BEV_EVENT_EOF" << endl;

                Status *status = (Status *)ctx;
                cout<<"server recv = "<<status->recvNum<<endl;
                cout<<"server write = "<<status->writeNum<<endl;
                delete status;

                bufferevent_free(bev);
        }
}

void listen_cb(evconnlistener *ev, evutil_socket_t lfd, 
                        struct sockaddr *sin, int socklen, void *arg){
        sockaddr_in *csin = (sockaddr_in *)sin;
        char ip[16] = {0};
        evutil_inet_ntop(sin->sa_family,  &csin->sin_addr, ip, sizeof(ip)-1);
        cout<<"client ip is "<<ip<<" port is "<<ntohs(csin->sin_port)<<endl;

        //1 创建一个bufferevent 用来通讯
        event_base *base = (event_base *)arg;
        bufferevent *bev = bufferevent_socket_new(base, lfd, BEV_OPT_CLOSE_ON_FREE);

        Status *status = new Status(); 
        status->p = new z_stream();
        inflateInit(status->p);

        //2 添加输入过滤器 并设置输入回调
        bufferevent *bev_filter = bufferevent_filter_new(bev, 
                        filter_in,  //输入过滤函数
                        NULL, //输出过滤
                        BEV_OPT_CLOSE_ON_FREE,//关闭filter同时关闭bufferevent
                        0, //清理回调
                        status // 传递参数
                        );

        //3 设置回调 读取 事件(处理连接断开) 
        bufferevent_setcb(bev_filter, read_cb, NULL, event_cb, status);
        bufferevent_enable(bev_filter,EV_READ|EV_WRITE);

}

int main(int argc, char const *argv[]){
#ifdef _WIN32
        //初始化socket库
        WSADATA wsa;
        WSAStartup(MAKEWORD(2,2), &wsa);
#else
        //忽略管道信号 发送数据给已关闭的socket
        if(signal(SIGPIPE,SIG_IGN) == SIG_IGN){
                return 1;
        }
#endif
        event_base *base = event_base_new();

        sockaddr_in sin;
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(8080);

        evconnlistener *ev = evconnlistener_new_bind(base, listen_cb, base, 
                LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE_PORT, -1,
                (sockaddr *)&sin, sizeof(sin));

        event_base_dispatch(base);

        evconnlistener_free(ev);

        event_base_free(base);

        return 0;
}
